/*
 * @Author: Once day
 * @Date: 2022-09-24 20:44
 * @LastEditTime: 2022-09-28 00:11:59
 * Encoder=utf-8,Tabsize=4,Eol=\r\n.
 * Email:once_day@qq.com
*/
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/socket.h>
#include "uds.h"
#include "easycall.h"

volatile int g_pcall_run_flag = 1;
int g_pcall_shell_mode=0;
char g_pcall_get_buf[EASY_CALL_MAX_LINE];
char g_pcall_put_buf[EASY_CALL_MAX_LINE];
uds_client_t *g_pcall_clnt;
    
typedef  struct{
    char *name;		     
    int  (*func)(char *);    
    char *doc;
}pcall_intner_cmd_t;

pcall_intner_cmd_t commands[] = {
    { "h",       NULL,    "Display this help"},
    { "help",    NULL,    "Display this help"},
    { "?",       NULL,    "Display this help"},
    { "q",       NULL,    "Quit using Ecall"},
    { "quit",    NULL,    "Quit using Ecall"},
    { "debug",   NULL,    "Set debug mode"},
    { "history", NULL, "List editline history"},
    { "list",    NULL,    "List dynamic lib"},
    { "l",       NULL,    "Load dynamic lib"},
    { "load",    NULL,    "Load dynamic lib"},
    { "s",       NULL,      "Show dynamic lib's symbols"},
    { "symbol",  NULL,      "Show dynamic lib's symbols"},
    { "sh",     NULL,      "Run shell cmd"},
    { "shell",  NULL,      "Run shell cmd"},
    { "md.b",   NULL,  "Get value as byte"},   
    { "md.w",   NULL,  "Get value as word"}, 
    { "md.l",   NULL,  "Get value as long"}, 
    { "nm.b",   NULL,  "Get value as byte"},   
    { "nm.w",   NULL,  "Get value as word"}, 
    { "nm.l",   NULL,  "Get value as long"}, 
    { "stack",  NULL,           "show stack"},
    { "status", NULL,         "show status"},
    { "maps",   NULL,         "show maps"},
    { NULL, NULL, NULL },
};
/**
 * @function: pcall_show_license
 * @description: 显示开源信息
 * @return {*}
 * @author: yuri
 */
void pcall_show_license(void)
{
    int ret = system("clear");
    ret = ret;
    printf("--------------------PCALL START------------------------\n");
    printf("AUTHOR: Once day\n");
    printf("VERSION: V1.0\n");
    printf("BUILD: "__DATE__" "__TIME__"\n");
    printf("Permission is granted to anyone to use this software\n");
    printf("for any purpose on any computer system but :\n");
    printf("1.The authors are not responsible for the consequences \n");
    printf("of use of this software\n");
    printf("2.The origin of this software must not be misrepresented,\n");
    printf("either by explicit claim or by omission\n");
    printf("--------------------PCALL END--------------------------\n");        
}

/**
 * @function: ecall_stripwhite
 * @description: 去空格
 * @param {char} *string
 * @return {*}
 * @author: yuri
 */
static char *pcall_stripwhite(char *string)
{
    char *s, *t;
    for (s = string; isspace(*s); s++) ;
    if (*s == 0)
	return s;
    t = s + strlen(s) - 1;
    while (t > s && isspace(*t))
	t--;
    *++t = '\0';
    return s;
}
/**
 * @function: pcall_longest_common_prefix
 * @description: 寻找最长前缀
 * @param 
 * @return {*}
 * @author: yuri
 */
char * pcall_longest_common_prefix(char ** strs, int strsSize)
{
    int i,j;
    if(!strsSize)return "";
    int minLen = strlen(strs[0]);
    for(i = 1; i < strsSize; ++i)
    {
        if(minLen > strlen(strs[i]))
        {
        minLen = strlen(strs[i]);
        }
    }
    char* result = (char*)malloc(sizeof(char) * (minLen+1));
    result[0] = 0;
    for(i = 0; i < minLen; ++i)
    {
        for(j = 1; j < strsSize; ++j)
        {
            if(strs[j][i] != strs[0][i])
            return result;
        }
        result[i] = strs[0][i];
        result[i+1] = 0;
    }
    return result;
}
/**
 * @function: pcall_command_generator
 * @description: 自动补全回调
 * @param {char} *text
 * @param {int} state
 * @return {*}
 * @author: yuri
 */
char *pcall_command_generator(const char *text, int state)
{
    const int match_max_nums=EASY_CALL_MAX_CMD_SHOW;
    static int list_index, len;
    int match;
    char* match_name[EASY_CALL_MAX_CMD_SHOW];
    char *name;
    if (!state) {
        list_index = 0;
        match=0;
        len = strlen(text);
        memset(match_name,0,list_index);
    }
    while ((name = commands[list_index].name)) {
	    list_index++;
        if (strncmp(name, text, len) == 0 && match <match_max_nums){
            match_name[match]=name;
            match++;
        }
    }
    if(match == 1){
        return strdup(match_name[0]);
    }else if(match == 0){
        return NULL;
    }else{
        return pcall_longest_common_prefix(match_name,match);
    }
}

/**
 * @function: 列出候选项
 * @description: 
 * @param {char} *token
 * @param {char} *
 * @return {*}
 * @author: yuri
 */
static int pcall_rl_list_possib(char *token, char ***av)
{
    int i,num=0, total = 0;
    char *name;
    char **copy;
    num= sizeof(commands)/sizeof(pcall_intner_cmd_t);
    copy = malloc(num * sizeof(char *));
    for(i = 0 ;i<sizeof(commands)/sizeof(pcall_intner_cmd_t)-1;i++ )
    {
        name=commands[i].name;
        if (!strncmp(name, token, strlen (token))) {
            copy[total] = strdup(name);
            total++;
        }
    }
    *av = copy;
    return total;
}
char **pcall_completion(const char *text, int start, int end)
{
    char **matches = NULL;
    if (start == 0)
	matches = rl_completion_matches(text, pcall_command_generator);
    return matches;
}
void pcall_initialize_readline(void)
{
    rl_readline_name = "pcall";
    rl_attempted_completion_function = pcall_completion;
    rl_set_list_possib_func(&pcall_rl_list_possib);
}
/**
 * @function: pcall_client_cli
 * @description: 命令行处理
 * @param {char} *buf
 * @return {*}
 * @author: Once day
 */
int pcall_client_cli(char *buf)
{
    int ret=0;
    char *line, *s;
    line = readline("pcall$ ");
    if (!line)
        return 0;
    s = pcall_stripwhite(line);

    if (strlen(s) >0)
    {
        if(!strcmp(s,"q")||!strcmp(s,"quit"))
        {
            ret=strlen(s);
            strcpy(buf,s);
            ret = -1;
        }else if(!strcmp(s,"sh")||!strcmp(s,"shell"))
        {
            g_pcall_shell_mode=!g_pcall_shell_mode;
            if(g_pcall_shell_mode == 1 )
            {
                printf("shell mode on\n");
            }else{
                printf("shell mode off\n");
            }
        }
        else{
            ret=strlen(s);
            strcpy(buf,s);
        }
    }
    free(line);
    return ret;
}

void * pcall_client_recv(void* sc)
{
    int bytes;
    uds_client_t* c = (uds_client_t*)sc;
    while(g_pcall_run_flag) {
        memset(g_pcall_get_buf,0,sizeof(g_pcall_get_buf));
        bytes = recv_data(c->sockfd, (char *)g_pcall_get_buf, sizeof(g_pcall_get_buf)-1, 0);
        if (bytes <= 0) {
            printf("Error: receive response error\n");
            g_pcall_run_flag = 0;
            continue;
        }
        g_pcall_get_buf[bytes] = '\0';
        printf("\n%s\n",g_pcall_get_buf);
    }
    return (void*)c;
}

void pcall_send_cmd(char *cmd)
{
    uds_request_put_msg_t req;
    uds_command_t *res;
    req.common.command = CMD_GET_MESSAGE;
    if(g_pcall_shell_mode == 0){
        req.common.data_len = strlen(cmd);
        memset(req.data,0,UDS_PUT_MSG_SIZE-1);
        snprintf((char *)req.data, UDS_PUT_MSG_SIZE-1, "%s", cmd);
    }
    else{
        req.common.data_len = strlen(cmd)+3;
        memset(req.data,0,UDS_PUT_MSG_SIZE-1);
        snprintf((char *)req.data, UDS_PUT_MSG_SIZE-1, "sh %s", cmd);
    }
    req.data[UDS_PUT_MSG_SIZE-1] = 0;
    res = (uds_command_t *)client_send_request(g_pcall_clnt, (uds_command_t *)&req);
    if (res == NULL) {
        printf("client: send request error\n");
        g_pcall_run_flag =0 ;
    }
    usleep(50*1000);
}
int main(int argc ,char** argv)
{
    int ch;
    int ret;
    pthread_t thread_id = 0;
    char cmd[EASY_CALL_MAX_LINE];
    char ip[20]="127.0.0.1";
    pcall_show_license();
    g_pcall_clnt = client_init(ip, 10);
    if (g_pcall_clnt == NULL) {
        printf("client: init error\n");
        return -1;
    }
    if (pthread_create(&thread_id, NULL, pcall_client_recv, g_pcall_clnt) != 0) {
        perror("pthread_create error");
        close(g_pcall_clnt->sockfd);
        return -1;
    }
    while ((ch = getopt(argc , argv , "s:l:e:")) != -1)
    {
        switch (ch) 
        {
        case 's':
            memset(ip,0,20);
            strcpy(ip,(char*)optarg);
            break;
        case 'l':
            memset(cmd,0,sizeof(cmd));
            sprintf(cmd,"load %s",(char*)optarg);
            pcall_send_cmd(cmd);
            usleep(10*1000);
            break;
        case 'e':
            memset(cmd,0,sizeof(cmd));
            sprintf(cmd,"%s",(char*)optarg);
            pcall_send_cmd(cmd);
            usleep(10*1000);
            break;
        default:
            break;
        }
    }

    pcall_initialize_readline();
    while ( g_pcall_run_flag )
    {
        memset(g_pcall_put_buf,0,sizeof(g_pcall_put_buf));
        ret = pcall_client_cli(g_pcall_put_buf);
        if(ret == 0) {
            continue;
        }else if(ret<0){
            break;
        }
        else{
            pcall_send_cmd(g_pcall_put_buf);
        }
    }
    client_close(g_pcall_clnt);
    return 0;
}
